package fastas3
{
	import fastas3.tween.TweenMax;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;
	import flash.utils.getDefinitionByName;
	
	/**
	 * FastObject
	 * 
	 * @author Suguru Namura
	 * @version 0.1.0
	 */
	public dynamic class FastObject
	{
		// クラス名検索のための正規表現
		private static const CLASS_REGEX:RegExp = /^([^#]+)/;
		// 名前検索のための正規表現
		private static const NAME_REGEX:RegExp = /#([^\[]+)/;
		// クラス検索に使用するパッケージ群
		private static const PACKAGES:Array = ["flash.display"];
		
		// ステージへの参照
		private static var STAGE:Stage;
		// データ保管用弱参照ディクショナリ
		private static var DATA_REF:Dictionary = new Dictionary(true);
		// クラスキャッシュ
		private static var CLASS_CACHE:Object = new Object();
		// 実体参照用キャッシュ
		public static var OBJECT_CACHE:Dictionary = new Dictionary(true);
		
		// データキャッシュ
		private static var DATA_CACHE:Dictionary = new Dictionary(true);
		// アトリビュートキャッシュ
		private static var ATTR_CACHE:Dictionary = new Dictionary(true);
		
		// 親オブジェクト
		private var _parent:Object;
		// セレクタ
		private var _selector:Object;
		// 選択された対象一覧
		private var _selections:Array;
		// 選択された対象の数
		private var _length:int;
		
		/**
		 * パッケージ検索のためのパッケージを追加します。
		 * flash.display パッケージはデフォルトで追加されています。
		 * 
		 * @param packageName パッケージ名 (ex. flash.display)
		 */
		public static function addPackage(packageName:String):void {
			PACKAGES.push(packageName);
		}
		
		/**
		 * FastObject を構成します。
		 */
		public function FastObject(selector:Object, parent:Object = null) {
			
			_selector = selector;
			_parent = (parent == null) ? STAGE : parent;
			
			// プラグインであれば、プラグインをロードする
			if (selector is FastPlugin) {
				(selector as FastPlugin).loadPlugin();
				return;
			}
			
			if (selector is Event) {
				selector = (selector as Event).currentTarget;
			}
			
			// ステージであればステージを設定する
			if (selector is Stage) {
				STAGE = selector as Stage;
				_selections = [STAGE];
				_length = 1;
				return;
			}
			
			// DisplayObject の場合は、単体参照で作成する
			if (selector is DisplayObject) {
				var d:DisplayObject = selector as DisplayObject;
				if (STAGE == null && d.stage != null) {
					STAGE = d.stage;
				}
				_selections = new Array(1);
				_selections[0] = d;
				_length = 1;
				OBJECT_CACHE[selector] = this;
				return;
			}
			
			// クラスが指定されている場合は、クラスを初期化する
			if (selector is Class) {
				var o:Object = (selector as Class)();
				if (o is DisplayObject) {
					_selections = [o];
					_length = 1;
					return;
				} else {
					throw new Error("Cannot instantiate object that does not extend DisplayObject");
				}
			}
			
			// 文字列の場合は、オブジェクトの検索を実施する
			if (selector is String) {
				var s:String = selector as String;
				if (_parent is Array) {
					_selections = searchObjects(s.split(" "), _parent as Array);
				} else if (_parent is DisplayObject) {
					_selections = searchObjects(s.split(" "), [_parent]);
				}
				_length = _selections.length;
				return;
			}
			
			throw new Error("Invalid selector type.");
		}
		
		/**
		 * オブジェクトを検索します。
		 * 
		 * @param selectors 検索対象のオブジェクト一覧
		 * @param matched マッチした対象が格納される配列
		 */
		private function searchObjects(selectors:Array, matched:Array):Array {
			// 対象が存在しなければパス
			if (selectors.length == 0) {
				return [];
			}
			
			var onlyThese:Boolean = false;
			var query:Query = new Query();
			
			// すべての対象に対して検索を実施
			for (var i:int = 0; i < selectors.length; i++) {
				
				var selector:String = selectors[i];
				
				// セレクタが > で区切られる場合は、
				// 直下のオブジェクトのみを対象とする
				if (selector == ">") {
					onlyThese = true;
					continue;
				}
				
				var newMatched:Array = new Array();

				// クラス名を取得
				var className:String = CLASS_REGEX.test(selector) ?
						selector.match(CLASS_REGEX)[0] : null;
			
				// プロパティ名を取得
				var name:String = NAME_REGEX.test(selector) ?
						selector.match(NAME_REGEX)[1] : null;
						
				// プロパティ名の正規表現を構成
				query.name = name;
				if (name != null && name.indexOf("*") >= 0) {
					query.nameRegex = new RegExp("^" + name.replace("*", ".*") + "$");
				} else {
					query.nameRegex = new RegExp("^" + name + "$");
				}

				// クラスの実体を検索
				if (className != null) {
					query.clazz = findClass(className);
				}
				
				// 検索の実行				
				traverse(query, matched, newMatched, onlyThese);
				
				// 次の検索でさらに絞り込むため、
				// マッチした結果を確保しておく
				matched = newMatched;
				if (matched.length == 0) {
					break;
				}
				
				onlyThese = false;
			}
			
			return matched;
		}
		
		/**
		 * 指定したターゲットを再帰的に検索します。
		 * 
		 * @param query 検索クエリ
		 * @param targets 検索対象一覧
		 * @param result ヒットした結果を投入する配列
		 * @param onlyThese 階層検索をするかどうか
		 */
		private function traverse(query:Query, targets:Array, result:Array, onlyThese:Boolean):void {
			
			for (var i:int = 0; i < targets.length; i++) {
				
				var o:Object = targets[i];
				
				// 表示用オブジェクトの場合
				if (o is DisplayObject) {
					var d:DisplayObject = o as DisplayObject;
					
					// クラスが一致する場合
					if (query.clazz != null && (d is query.clazz)) {
						// プロパティ名が条件を満たせば、ヒットとみなす
						if (query.name == null || query.nameRegex.test(d.name)) {
							result.push(d);
						}
					}
					// プロパティ名が条件を満たせば、ヒットとみなす
					else if (query.name != null && query.nameRegex.test(d.name)) {
						result.push(d);
					}
				}
				
				// DisplayObjectCongtainer であればさらに下階層をチェック
				if (!onlyThese && o is DisplayObjectContainer) {
					var c:DisplayObjectContainer = (o as DisplayObjectContainer);
					if (c.numChildren > 0) {
						var t:Array = new Array(c.numChildren);
						for (var j:int = 0; j < c.numChildren; j++) {
							t[j] = c.getChildAt(j);
						}
						// 再帰的に検索を実施
						traverse(query, t, result, false);
					}
				}
			}
		}
		
		/**
		 * 名称からクラスを検索します。
		 * @param name クラス名
		 * @return クラス
 		 */
		private function findClass(name:String):Class {
			if (CLASS_CACHE.hasOwnProperty(name)) {
				// キャッシュにある場合は、キャッシュから返却
				return CLASS_CACHE[name];
			}
			try {
				// 名称から取得を試みる
				return CLASS_CACHE[name] = getDefinitionByName(name);
			} catch (e:Error) {
			}
			// 検索対象のパッケージ一覧を検索し、
			// クラス名が一致すればクラスとする
			for (var i:int = 0; i < PACKAGES.length; i++) {
				try {
					return CLASS_CACHE[name] = getDefinitionByName(PACKAGES[i] + "." + name);
				} catch (e:Error) {
				}
			}
			// 存在しなければエラーをスロー
			throw new Error("Class " + name + " was not found in defined packages");
		}
		
		/**
		 * セレクタによって選択されたオブジェクトの数を取得します。
		 * @return オブジェクト数
		 */
		public function get length():int {
			return _length;
		}
		
		/**
		 * 選択されたオブジェクトを起点として、クエリでさらに検索します。
		 * @param クエリ文字列
		 * @return FastObject
		 */
		public function filter(selector:String):FastObject {
			return new FastObject(selector, _selections);
		}
		
		/**
		 * 選択されたオブジェクトに対して個別に関数を実行します。
		 * 関数の１つ目の引数には、オブジェクトが渡されます。
		 * 
		 * @param handle 実行する関数
		 * @return FastObject
		 */
		public function foreach(handle:Function):FastObject {
			for (var i:int = 0; i < _selections.length; i++) {
				handle(_selections[i]);
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトを表示状態にします。
		 * 
		 * @return FastObject
		 */
		public function show():FastObject {
			for (var i:int = 0; i < _selections.length; i++) {
				(_selections[i] as DisplayObject).visible = true;
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトの非表示状態にします。
		 * 
		 * @return FastObject
		 */
		public function hide():FastObject {
			for (var i:int = 0; i < _selections.length; i++) {
				(_selections[i] as DisplayObject).visible = false;
			}
			return this;
		}
		
		/**
		 * この選択対象のプロパティオブジェクトを取得します。
		 * 
		 * @param name プロパティ名
		 */
		public function attr(name:String):FastAttr {
			return FastAttr.createAttr(this, name);
			//return new FastAttr(this, name);
		}
		
		/**
		 * 選択されたオブジェクトのプロパティを取得します。
		 * 複数選択されている場合は、最初のオブジェクトが対象となります。
		 * 
		 * @param name プロパティ名
		 * @return プロパティ値。存在しなければ null
		 */
		internal function _getAttr(name:String):Object {
			if (length == 0) return null;
			var o:Object = _selections[0];
			if (o.hasOwnProperty(name)) {
				return o[name];
			} else {
				return null;
			}
		}
		
		/**
		 * 選択されたオブジェクトにプロパティを設定します。
		 * 
		 * @param name プロパティ名
		 * @param value プロパティ値
		 * @return FastObject
		 */
		internal function _setAttr(name:String, value:Object):FastObject {
			for (var i:int = 0; i < length; i++) {
				var obj:Object = _selections[i];
				obj[name] = value;
			}
			return this;
		}
		
		/**
		 * 対象オブジェクトのプロパティの現在の値を保管します。
		 * 保管後に変更があっても、保管先には影響がありません。
		 * 
		 * @param name プロパティ名
		 */
		internal function _saveAttr(name:String):FastObject {
			for (var i:int = 0; i < length; i++) {
				__setData(_selections[i], "_attr_" + name,  _selections[i][name]);
			}
			return this;
		}
		
		/**
		 * 対象オブジェクトのプロパティが保管されている場合は、
		 * 保管されている値を取得します。
		 * 複数のオブジェクトが対象の場合は、最初のオブジェクトの結果を返します。
		 * プロパティが保管されていない場合は、現在のプロパティ値を返します。
		 * 
		 * @param name プロパティ名
		 */
		internal function _loadAttr(name:String):Object {
			if (_hasData("_attr_" + name)) {
				return _getData("_attr_" + name);
			} else {
				return _getAttr(name);
			}
		}
		
		/**
		 * 対象のオブジェクトのプロパティが保管されている場合は、
		 * プロパティ値を保管されている値に戻します。
		 * 保管されていない場合は、何も変更されません。
		 * 
		 * @param name プロパティ値
		 */
		internal function _restoreAttr(name:String):FastObject {
			for (var i:int = 0; i < length; i++) {
				__setData(
					_selections[i],
					"_attr_" + name, 
					__getData(_selections[i], "_attr_" + name)
				);
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトのプロパティに
		 * 指定した値を加算します。
		 * 
		 * @param name プロパティ名
		 * @param value 加算するプロパティ値
		 * @return FastObject
		 */
		internal function _addAttr(name:String, value:Object):FastObject {
			for (var i:int = 0; i < length; i++) {
				var obj:Object = _selections[i];
				obj[name] += value;
			}
			return this;
		}
		
		/**
		 * 選択されているオブジェクトのカスタムデータを取得します。
		 * 
		 * @param name データ名
		 * @return FastData
		 */
		public function data(name:String):FastData {
			return FastData.createData(this, name);
			//return new FastData(this, name);
		}
		
		/**
		 * 選択せれたオブジェクトのカスタムデータ値を取得します。
		 * 複数選択されている場合は、最初のオブジェクトの値が返されます。
		 * 
		 * @param name データ名
		 * @return データ値
		 */
		internal function _getData(name:String):Object {
			if (length == 0) {
				return null;
			}
			return __getData(_selections[0], name);
		}
		
		/**
		 * 選択されたオブジェクトにカスタムデータ値を設定します。
		 * 
		 * @param name データ名n
		 * @param value データ値
		 * @return FastObject
		 */
		internal function _setData(name:String, value:Object):FastObject {
			for (var i:int = 0; i < length; i++) {
				__setData(_selections[i], name, value);
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトのカスタムデータ値を加算します。
		 * 
		 * @param name データ名
		 * @param value 加算値
		 * @return FastObject
		 */
		internal function _addData(name:String, value:Object):FastObject {
			for (var i:int = 0; i < length; i++) {
				__addData(_selections[i], name, value);
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトからカスタムデータを除去します。
		 * 
		 * @param name データ名
		 * @return FastObject
		 */
		internal function _removeData(name:String):FastObject {
			for (var i:int = 0; i < length; i++) {
				__removeData(_selections[i], name);
			}
			return this;
		}
		
		/**
		 * カスタムデータの存在を確認します。
		 * 複数のオブジェクトが選択されている場合は
		 * 先頭のオブジェクトがチェックの対象となります。
		 * 
		 * @param name データ名
		 */
		internal function _hasData(name:String):Boolean {
			if (_length == 0) {
				return false;
			}
			return __hasData(_selections[0], name);
		}
		
		/**
		 * オブジェクトのカスタムデータの有無を確認します。
		 * 
		 * @param o 対象オブジェクト
		 * @param name データ名
		 */
		private function __hasData(o:Object, name:String):Boolean {
			var obj:Object = DATA_REF[o];
			if (obj == null) {
				return false;
			}
			return obj.hasOwnProperty(name);
		}
		
		/**
		 * オブジェクトのカスタムデータを設定します。
		 * 
		 * @param o 対象オブジェクト
		 * @param name データ名
		 * @param value データ値
		 */
		private function __setData(o:Object, name:String, value:Object):void {
			var obj:Object = DATA_REF[o];
			if (obj == null) {
				obj = new Object();
				DATA_REF[o] = obj;
			}
			obj[name] = value;
		}
		
		/**
		 * オブジェクトのカスタムデータを削除します。
		 * 
		 * @param o 対象オブジェクト
		 * @param name データ名
		 */
		private function __removeData(o:Object, name:String):void {
			var obj:Object = DATA_REF[o];
			if (obj != null && obj.hasOwnProperty(name)) {
				delete obj[name];
			}
		}
		
		/**
		 * オブジェクトのカスタムデータを取得します。
		 * 
		 * @param o 対象オブジェクト
		 * @param name データ名
		 */
		private function __getData(o:Object, name:String):Object {
			var obj:Object = DATA_REF[o];
			if (obj != null && obj.hasOwnProperty(name)) {
				return obj[name];
			} else {
				return null;
			}
		}
		
		/**
		 * オブジェクトのカスタムデータを加算します。
		 * 
		 * @param o 対象オブジェクト
		 * @param name データ名
		 * @param value 加算値
		 */
		private function __addData(o:Object, name:String, value:Object):void {
			var arr:Array = __getData(o, name) as Array;
			if (arr == null) {
				arr = new Array();
				__setData(o, name, arr);
			}
			arr.push(value);
		}
		
		/**
		 * 選択されたオブジェクトに対してイベントハンドラを登録します。
		 * @param type イベント名
		 * @param handle イベントハンドラ関数
		 */
		public function bind(type:String, handle:Function):FastObject {
			for (var i:int = 0; i < length; i++) {
				var e:EventDispatcher = _selections[i] as EventDispatcher;
				if (e != null) {
					e.addEventListener(type, handle);
					__addData(e, "__" + type, handle);
				}
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトからイベントハンドラを解除します。
		 * 
		 * @param type イベント名
		 * @param handle 解除する関数
		 */
		public function unbind(type:String, handle:Function = null):FastObject {
			for (var i:int = 0; i < length; i++) {
				var e:EventDispatcher = _selections[i] as EventDispatcher;
				if (e != null) {
					if (handle == null) {
						var arr:Array = __getData(e, "__" + type) as Array;
						if (arr != null) {
							for (var j:int = 0; j < arr.length; j++) {
								e.removeEventListener(type, arr[j] as Function);
							}
							__removeData(e, "__" + type);
						}
					} else {
						e.removeEventListener(type, handle);
						arr = __getData(e, "__" + type) as Array;
						if (arr != null) {
							var idx:int = arr.indexOf(handle);
							if (idx >= 0) {
								arr.splice(idx, 1);
							}
						}
					}
				}
			}
			return this;
		}
		
		/**
		 * bind(Event.ENTER_FRAME, handle);
		 * へのショートカットです。
		 */
		public function enterFrame(handle:Function):FastObject {
			return bind(Event.ENTER_FRAME, handle);
		}
		
		/**
		 * bind(MouseEvent.CLICK, handle);
		 * へのショートカットです。
		 */
		public function click(handle:Function):FastObject {
			return bind(MouseEvent.CLICK, handle);
		}
		
		/**
		 * bind(MouseEvent.DOUBLE_CLICK, handle)
		 * へのショートカットです。
		 */
		public function doubleClick(handle:Function):FastObject {
			return bind(MouseEvent.DOUBLE_CLICK, handle);
		}
		
		/**
		 * bind(MouseEvent.MOUSE_DOWN, handle)
		 * へのショートカットです。
		 */
		public function mouseDown(handle:Function):FastObject {
			return bind(MouseEvent.MOUSE_DOWN, handle);
		}
		
		/**
		 * bind(MouseEvent.MOUSE_UP, handle)
		 * へのショートカットです。
		 */
		public function mouseUp(handle:Function):FastObject {
			return bind(MouseEvent.MOUSE_UP, handle);
		}
		
		/**
		 * bind(MouseEvent.MOUSE_MOVE, handle)
		 * へのショートカットです。
		 */
		public function mouseMove(handle:Function):FastObject {
			return bind(MouseEvent.MOUSE_MOVE, handle);
		}
		
		/**
		 * bind(Event.RESIZE, handle)
		 * へのショートカットです。
		 */
		public function resize(handle:Function):FastObject {
			return bind(Event.RESIZE, handle);
		}
		
		/**
		 * マウスが対象オブジェクト内で押され、
		 * カーソルのオブジェクトの範囲内でボタンが離れても実行される
		 * イベントリスナを登録します。
		 */
		public function releaseOutside(handle:Function):FastObject {
			bind(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
				var d:DisplayObject = (e.currentTarget as DisplayObject);
				d.addEventListener("__releaseOutside", function(ee:MouseEvent):void {
					d.removeEventListener("__releaseOutside", arguments.callee);
					handle(ee);
				});
				STAGE.addEventListener(MouseEvent.MOUSE_UP, function(ee:MouseEvent):void {
					STAGE.removeEventListener(MouseEvent.MOUSE_UP, arguments.callee);
					var eee:MouseEvent = new MouseEvent(
							"__releaseOutside", ee.bubbles, ee.cancelable,
							d.mouseX, d.mouseY,
							ee.relatedObject,
							ee.ctrlKey, ee.altKey, ee.shiftKey, ee.buttonDown, ee.delta); 
					d.dispatchEvent(eee);
				});
			});
			return this;
		}
		
		/**
		 * bind(MouseEvent.ROLL_OVER, overHandle);
		 * bind(MouseEvent.ROLL_OUT, outHandle);
		 * へのショートカットです。
		 */
		public function roll(overHandle:Function, outHandle:Function):FastObject {
			bind(MouseEvent.ROLL_OVER, overHandle);
			bind(MouseEvent.ROLL_OUT, outHandle);
			return this;
		}
		
		/**
		 * bind(MouseEvent.MOUSE_OVER, overHandle);
		 * bind(MouseEvent.MOUSE_OUT, outHandle);
		 * へのショートカットです。
		 */
		public function hover(overHandle:Function, outHandle:Function):FastObject {
			bind(MouseEvent.MOUSE_OVER, overHandle);
			bind(MouseEvent.MOUSE_OUT, outHandle);
			return this;
		}
		
		/**
		 * bind(KeyboardEvent.KEY_DOWN, handle);
		 * へのショートカットです。
		 */
		public function keyDown(handle:Function):FastObject {
			bind(KeyboardEvent.KEY_DOWN, handle);
			return this;
		}
		
		/**
		 * bind(KeyboardEvent.KEY_UP, handle);
		 * へのショートカットです。
		 */
		public function keyUp(handle:Function):FastObject {
			bind(KeyboardEvent.KEY_UP, handle);
			return this;
		}
		
		/**
		 * 選択されたオブジェクトに対して、toggle が呼び出されるたびに
		 * 第一引数の関数と第二引数の関数を交互に実行します。
		 * 
		 * @param 奇数回に実行される関数
		 * @param 偶数回に実行される関数
		 * @return FastObject
		 */
		public function toggle(on:Function, off:Function):FastObject {
			for (var i:int = 0; i < length; i++) {
				var d:DisplayObject = _selections[i] as DisplayObject;
				var b:Boolean = (__hasData(d, "__toggle") ? __getData(d, "__toggle") : true);
				if (b) {
					on(d);
				} else {
					off(d);
				}
				__setData(d, "__toggle", !b);
			}
			return this;
		}

		/**
		 * 選択されたオブジェクトの表示順位を
		 * 所属するオブジェクトの中で最前列まで移動します。
		 * 
		 * @return FastObject
		 */
		public function moveTop():FastObject {
			for (var i:int = 0; i < length; i++) {
				var d:DisplayObject = _selections[i] as DisplayObject;
				if (d.parent != null) {
					d.parent.addChild(d);
				}
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトに対して
		 * ColorTransform を設定します。
		 * 
		 * @param transform ColorTransform
		 * @return FastObject
		 */
		public function color(
				redMultiplier:Number = 1.0,
				greenMultiplier:Number = 1.0,
				blueMultiplier:Number = 1.0,
				alphaMultiplier:Number = 1.0,
				redOffset:Number = 0,
				greenOffset:Number = 0,
				blueOffset:Number = 0,
				alphaOffset:Number = 0):FastObject {
			var tr:ColorTransform = new ColorTransform(
					redMultiplier,
					greenMultiplier,
					blueMultiplier,
					alphaMultiplier,
					redOffset,
					greenOffset,
					blueOffset,
					alphaOffset
			);
			for (var i:int = 0; i < length; i++) {
				var d:DisplayObject = _selections[i] as DisplayObject;
				d.transform.colorTransform = tr;
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトに対して行列変換を実施します。
		 * 
		 * @param matrix 変換x行列
		 * @return FastObject
		 */	
		public function transform(matrix:Matrix):FastObject {
			for (var i:int = 0; i < length; i++) {
				var d:DisplayObject = _selections[i] as DisplayObject;
				d.transform.matrix = matrix;
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトに対して TweenFilterLite による
		 * Tween を登録します。
		 * 
		 * @param duration 時間
		 * @param param TweenFilterLite パラメータ
		 * @return FastObject
		 */
		public function tween(duration:Number, param:Object):FastObject {
			for (var i:int = 0; i < length; i++) {
				TweenMax.to(
					_selections[i],
					duration,
					param
				);
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトに対して個別に関数を実行し、
		 * 返却されたオブジェクトを TweenFilterLite に渡して
		 * Tween を登録します。
		 * 
		 * @param duration 時間
		 * @param handle 実行される関数
		 * @return FastObject
		 */
		public function tweenWith(duration:Number, handle:Function):FastObject {
			for (var i:int = 0; i < length; i++) {
				TweenMax.to(
					_selections[i],
					duration,
					handle(_selections[i])
				);
			}
			return this;
		}
		
		/**
		 * tween 関数の TweenFilterLite.from 版です。
		 * 
		 * @param duration 時間
		 * @param param TweenFilterLiteパラメータ
		 * @return FastObject
		 */
		public function tweenFrom(duration:Number, param:Object):FastObject {
			for (var i:int = 0; i < length; i++) {
				TweenMax.from(
					_selections[i],
					duration,
					param
				);
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトに対して
		 * TweenFilterLite の機能による遅延実行を行います。
		 * 
		 * @param seconds 遅延時間
		 * @param handle 実行される関数
		 * @return FastObject
		 */
		public function delay(seconds:Number, handle:Function):FastObject {
			for (var i:int = 0; i < length; i++) {
				TweenMax.delayedCall(
					seconds,
					handle,
					[_selections[i]]
				);
			}
			return this;
		}
		
		/**
		 * 指定したURLから情報をロードし、表示します。
		 * param にオブジェクトを渡すことでイベントリスナを登録できます。
		 * 
		 * <pre>
		 * {
		 * 		onComplete:function(l:Loader) {},
		 * 		onHttpStatus:function(e:HttpStatusEvent) {},
		 * 		onIoError:function(e:IoErrorEvent) {},
		 * 		onProgress:function(e:ProgressEvent) {}
		 * }
		 * </pre>
		 * 
		 * @param url ロードするURL
		 * @param param イベントハンドラオブジェクト
		 */
		public function load(url:String, param:Object = null):FastObject {
			for (var i:int = 0; i < length; i++) {
				if (_selections[i] is DisplayObjectContainer) {
					var c:DisplayObjectContainer = _selections[i] as DisplayObjectContainer;
					var loader:Loader = new Loader();
					loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
						if (param != null && param.hasOwnProperty("onComplete")) {
							param.onComplete(loader);
						}
					});
					if (param != null && param.hasOwnProperty("onHttpStatus")) {
						loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, param.onHttpStatus);
					}
					if (param != null && param.hasOwnProperty("onIoError")) {
						loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, param.onIoError);	
					}
					if (param != null && param.hasOwnProperty("onProgress")) {
						loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, param.onProgress);
					}
					loader.load(new URLRequest(url));
					c.addChild(loader);
				}
			}
			return this;
		}
		
		/**
		 * 選択されたオブジェクトをグレーアウトし、
		 * すべての操作を受け付けない状態にします。
		 * 
		 * @param color グレーアウトに使用する色
		 * @param alpha グレーアウトに使用するアルファ値
		 * @param duration グレーアウトに要する秒数
		 * @param clickToErase グレーアウトをクリックでキャンセルするか
		 * @param escToErase　グレーアウトをESCキーでキャンセルするか
		 * @return FastObject
		 */
		public function glayOn(
				color:uint = 0x000000,
				alpha:Number = 0.5,
				duration:Number = 0.5,
				clickToErase:Boolean = false):FastObject {

			return foreach(function(d:DisplayObject):void {
				if (__hasData(d, "__glay")) return;
				if (d is DisplayObjectContainer) {
					var c:DisplayObjectContainer = d as DisplayObjectContainer;
					var s:Sprite = new Sprite();
					__disableEvents(s, [
						MouseEvent.CLICK, MouseEvent.DOUBLE_CLICK, MouseEvent.MOUSE_DOWN,
						MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_UP, MouseEvent.MOUSE_OUT, MouseEvent.MOUSE_OVER,
						MouseEvent.MOUSE_WHEEL, MouseEvent.ROLL_OUT, MouseEvent.ROLL_OVER,
						KeyboardEvent.KEY_DOWN, KeyboardEvent.KEY_UP
					]);
					if (clickToErase) {
						$(s)
						.unbind(MouseEvent.CLICK)
						.click(function(e:MouseEvent):void {
							TweenMax.to(
								s,
								duration,
								{
									alpha: 0,
									onComplete: function():void {
										s.parent.removeChild(s);
									}
								}
							);
						});
					}
					s.alpha = 0;
					s.graphics.beginFill(color, alpha);
					if (d is Stage) {
						s.graphics.drawRect(0, 0, (d as Stage).stageWidth, (d as Stage).stageHeight);
					} else {
						s.graphics.drawRect(0, 0, c.width, c.height);
					}
					s.graphics.endFill();
					c.addChild(s);
					TweenMax.to(s, duration, { alpha: alpha });
				}
			});
		}
		
		/**
		 * グレーアウトしているオブジェクトの
		 * グレーアウトを解除します。
		 * 
		 * @param duration 解除に要する秒数
		 */
		public function glayOff(duration:Number = 0.5):FastObject {
			return foreach(function(d:DisplayObject):void {
				if (__hasData(d, "__glay")) {
					var s:DisplayObject = __getData(d, "__glay") as DisplayObject;
					TweenMax.to(
						s,
						duration,
						{
							alpha: 0,
							onComplete: function():void {
								s.parent.removeChild(s);
							}
						}
					);
				}
			});
		}
		
		/**
		 * 指定したイベントを無効化します。
		 * 
		 * @param o 対象オブジェクト
		 * @param types イベント一覧
		 */
		private function __disableEvents(o:DisplayObject, types:Array):void {
			for (var i:int = 0; i < types.length; i++) {
				o.addEventListener(types[i], __stopPropagation);
			}
		}
		
		/**
		 * イベント無効化用ハンドラ
		 */
		private function __stopPropagation(e:Event):void {
			e.stopPropagation();
		}
		
	}
}

/**
 * FastObject内部で検索に使われるクエリ情報を保持します。
 */
class Query {
	internal var clazz:Class;
	internal var name:String;
	internal var nameRegex:RegExp;
	public function toString():String {
		return "Class:" + clazz + " Name:" + name;
	}
}
